{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Image Manipulation with skimage"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This example builds a simple UI for performing basic image manipulation with [scikit-image](http://scikit-image.org/)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Stdlib imports\n",
    "from io import BytesIO\n",
    "\n",
    "# Third-party libraries\n",
    "from IPython.display import Image\n",
    "from ipywidgets import interact, interactive, fixed\n",
    "import matplotlib as mpl\n",
    "from skimage import data, filters, io, img_as_float\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's load an image from scikit-image's collection, stored in the `data` module. These come back as regular numpy arrays:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "i = img_as_float(data.coffee())\n",
    "i.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's make a little utility function for displaying Numpy arrays with the IPython display protocol:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def arr2img(arr):\n",
    "    \"\"\"Display a 2- or 3-d numpy array as an image.\"\"\"\n",
    "    if arr.ndim == 2:\n",
    "        format, cmap = 'png', mpl.cm.gray\n",
    "    elif arr.ndim == 3:\n",
    "        format, cmap = 'jpg', None\n",
    "    else:\n",
    "        raise ValueError(\"Only 2- or 3-d arrays can be displayed as images.\")\n",
    "    # Don't let matplotlib autoscale the color range so we can control overall luminosity\n",
    "    vmax = 255 if arr.dtype == 'uint8' else 1.0\n",
    "    with BytesIO() as buffer:\n",
    "        mpl.image.imsave(buffer, arr, format=format, cmap=cmap, vmin=0, vmax=vmax)\n",
    "        out = buffer.getvalue()\n",
    "    return Image(out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "arr2img(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's create a simple \"image editor\" function, that allows us to blur the image or change its color balance:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def edit_image(image, sigma=0.1, R=1.0, G=1.0, B=1.0):\n",
    "    new_image = filters.gaussian(image, sigma=sigma, multichannel=True)\n",
    "    new_image[:,:,0] = R*new_image[:,:,0]\n",
    "    new_image[:,:,1] = G*new_image[:,:,1]\n",
    "    new_image[:,:,2] = B*new_image[:,:,2]\n",
    "    return arr2img(new_image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can call this function manually and get a new image. For example, let's do a little blurring and remove all the red from the image:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "edit_image(i, sigma=5, R=0.1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But it's a lot easier to explore what this function does by controlling each parameter interactively and getting immediate visual feedback. IPython's `ipywidgets` package lets us do that with a minimal amount of code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lims = (0.0,1.0,0.01)\n",
    "interact(edit_image, image=fixed(i), sigma=(0.0,10.0,0.1), R=lims, G=lims, B=lims);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Browsing the scikit-image gallery, and editing grayscale and jpg images\n",
    "\n",
    "The coffee cup isn't the only image that ships with scikit-image, the `data` module has others. Let's make a quick interactive explorer for this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def choose_img(name):\n",
    "    # Let's store the result in the global `img` that we can then use in our image editor below\n",
    "    global img\n",
    "    img = getattr(data, name)()\n",
    "    return arr2img(img)\n",
    "\n",
    "# Skip 'load' and 'lena', two functions that don't actually return images\n",
    "interact(choose_img, name=sorted(set(data.__all__)-{'lena', 'load'}));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now, let's update our editor to cope correctly with grayscale and color images, since some images in the scikit-image collection are grayscale.  For these, we ignore the red (R) and blue (B) channels, and treat 'G' as 'Grayscale':"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lims = (0.0, 1.0, 0.01)\n",
    "\n",
    "def edit_image(image, sigma, R, G, B):\n",
    "    new_image = filters.gaussian(image, sigma=sigma, multichannel=True)\n",
    "    if new_image.ndim == 3:\n",
    "        new_image[:,:,0] = R*new_image[:,:,0]\n",
    "        new_image[:,:,1] = G*new_image[:,:,1]\n",
    "        new_image[:,:,2] = B*new_image[:,:,2]\n",
    "    else:\n",
    "        new_image = G*new_image\n",
    "    return arr2img(new_image)\n",
    "\n",
    "interact(edit_image, image=fixed(img), sigma=(0.0, 10.0, 0.1), \n",
    "               R=lims, G=lims, B=lims);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
